WinZip 10.0 Keygenning |
||
Date |
par "Graftal" |
|
jj / mois / aaaa |
Publié par Quequero |
|
Parce que la musique fait! |
Greffe, bon comme d'habitude, merci beaucoup! |
Parce que la musique fait! |
.... |
Courriel: latfarg@gmail.com Greffe @ irc.azzurra.org # crack-it #cryptorev #asm |
.... |
Difficulté |
( ) Débutants (X) Intermédiaire () Avancé () Master |
introduction |
Bonjour à tous. Dans ce tutoriel nous traiterons d'un programme historique tel que WinZip: arrivés à la dixième incarnation, les programmeurs ont décidé de (enfin) changer la routine
de protection: et c'est là que nous intervenons. Asseyez-vous et profitez de la balade. =)
Pièce jointe
Outils d'occasion |
Les outils utilisés:
Visual Studio 2003 + OpenSSL pour coder le keygen.
RSA Tool v2 facilement trouvé sur goooogle =)
URL ou FTP du programme |
Eh bien, il ne faut pas beaucoup d'imagination pour le comprendre.
Rédaction |
Très bien, commençons immédiatement à ouvrir le programme avec ollydbg et notons qu'il n'est ni packagé ni rien. Le nag nous accueille d'une manière chaleureuse pour dire le moins, et nous le renvoyons à Enter Registration Code et insérons Graftal comme nom d'utilisateur et 1234567890 comme numéro de série. Bp habituel comme GetWindowText et GetDlgItemText, puis nous faisons OK. Le deuxième de ces points de base nous amène à la zone de code suivante:
004136FF |> PUSH 101; / Compte = 101 (257.); Cas 1 de l'interrupteur 00413704 |. MOV EDI, WINZIP32.0054F350; | ASCII «Graftal» 00413709 |. PUSH EDI; | Tampon => WINZIP32.0054F350 0041370A |. PUSH 0C80; | ControlID = C80 (3200.) 00413710 |. CALL DWORD PTR DS: [<& USER32.GetDlgItemTe>; \ GetDlgItemTextA ------------------- ~ snip ~ ------------------ 00413724 |. PUSH 27; / Nombre = 27 (39.) 00413726 |. MOV ESI, WINZIP32.0054F454; | ASCII "1234567890" 0041372B |. PUSH ESI; | Tampon => WINZIP32.0054F454 00413731 |. PUSH EBX; | hWnd 00413732 |. CALL DWORD PTR DS: [<& USER32.GetDlgItemTe>; \ GetDlgItemTextA |
Ce qui nous intéresse vraiment ici, ce sont les variables Count qui peuvent aussi nous donner une indication POSSIBLE de la longueur de la série. Nous en avons certainement besoin pour créer le keygen, ne serait-ce qu'en ce qui concerne la longueur maximale du surnom. Il faut maintenant trouver le point de contrôle du feuilleton: faisons attention ici, car le programme est très dispersé et il m'a fallu un moment pour trouver le bon endroit pour aller étudier et inverser. La meilleure chose à faire dans ces cas, c'est de passer par-dessus tout et tout le monde, pour voir s'il y a des variables ou des vérifications qui peuvent attirer notre attention: à ce stade, nous entrerons plus en détail. D'où nous en sommes maintenant dans le code, commençons à passer à un code comme celui-ci:
0041383D |. CALL WINZIP32.004133BB 00413842 |. TEST AL, AL 00413844 |. POP ECX 00413845 |. JE WINZIP32.004138E3; il saute beaucoup plus bas en nous montrant le mendiant 0041384B |. PUSH EDI 00413851 |. MOV EDI, WINZIP32.0051A808; ASCII "WinZip" 00413856 |. PUSH EDI 00413857 |. CALL WINZIP32.00499F4B 0041385D |. PUSH WINZIP32.0051A8F0; ASCII "SN1" 00413862 |. PUSH EDI 00413863 |. CALL WINZIP32.00499F4B 00413868 |. CALL WINZIP32.00412B2A 0041386D |. PUSH WINZIP32.0051A810; ASCII "winzip32.ini" 00413872 |. PUSH 0 00413874 |. PUSH 0 00413876 |. PUSH WINZIP32.0051A830; ASCII "rrs" |
On peut facilement deviner que l'essentiel du contrôle DEVRAIT avoir lieu dans l'appel que j'ai écrit ci-dessus: en fait, je dans le cas des données que nous avons saisies saute et donc le nom et le numéro de série ne sont pas enregistrés, si ce n'est en tant que périodiques corrects serait sauvé. Faisons F9 et recommençons OK, passons à l'étape et cette fois nous arrivons à l'appel 004133BB (celui ci-dessus en bref), entrons avec F7. À ce stade, nous sommes confrontés à beaucoup de code, pour la plupart absolument inutile. Comment le comprendre? Eh bien, il est important de parcourir tout le code la première fois, puis de trouver l'endroit où "creuser" =). En faisant cela, nous arrivons à ces instructions après un certain temps:
00413514 |. PUSH EDI; / Chaîne 00413515 |. CALL DWORD PTR DS: [<& KERNEL32.lstrlenA>]; \ lstrlenA 0041351B |. CMP EAX, 2 0041351E |. JL SHORT WINZIP32.0041352B 00413520 |. CMP EAX, 100 00413525 |. JG SHORT WINZIP32.0041352B |
Autre chose à retenir lors du codage du keygen: la longueur du nom d'utilisateur doit être comprise entre 2 et 0x100. Si nous continuons à avancer, nous remarquerons que notre numéro de série apparaîtra aussi, puis c'est tout, l'appel se termine. Nous devons maintenant trouver le bon appel pour faire un pas et analyser. Un premier choix à faire pourrait être d'analyser cet appel:
0041348E |. MOV EDI, WINZIP32.0054F350; ASCII «Graftal» 00413493 |. LEA EAX, DWORD PTR SS: [EBP-438] 00413499 |. MOV ECX, EDI 0041349B |. CALL WINZIP32.004124D2 |
Puisque nous trouvons notre nom d'utilisateur autour, il est possible que dans l'appel immédiatement en dessous, ou dans ceux qui le suivent, il y ait des instructions intéressantes à analyser. Le premier appel, cependant, même à un coup d'œil très superficiel, nous fait comprendre que rien d'important ne se passe là-dedans. Dans l'appel d'après, on trouve:
004E6980 / MOV EAX, DWORD PTR DS: [ECX] 004E6982 | MOV EDX, 7EFEFEFF 004E6987 | AJOUTER EDX, EAX 004E6989 | XOR EAX, FFFFFFFF 004E698C | XOR EAX, EDX 004E698E | AJOUTER ECX, 4 004E6991 | TEST EAX, 81010100 004E6996 | JE SHORT WINZIP32.004E6980 004E6998 | MOV EAX, DWORD PTR DS: [ECX-4] 004E699B | TEST AL, AL 004E699D | JE SHORT WINZIP32.004E69D1 004E699F | TEST AH, AH 004E69A1 | JE SHORT WINZIP32.004E69C7 004E69A3 | TEST EAX, 0FF0000 004E69A8 | JE SHORT WINZIP32.004E69BD 004E69AA | TEST EAX, FF000000 004E69AF | JE SHORT WINZIP32.004E69B3 004E69B1 \ JMP SHORT WINZIP32.004E6980 |
Après avoir inversé pendant un certain temps, nous commençons à comprendre que des valeurs telles que 0x7EFEFEFF et autres, en particulier dans ces contextes, sont des appels inutiles pour nos besoins. C'est un excellent critère pour éliminer certains des appels rencontrés en cours de route =). Cela dit (je trouve important d'expliquer certaines manières d '"éliminer" le code indésirable, surtout parce que c'est l'une des raisons qui poussent l'inverseur à abandonner le programme, ou qui en tout cas lui font perdre une myriade de temps ), nous utilisons une garde et très peu de cerveau: si la vérification de la longueur du nom d'utilisateur saisi est postérieure, il est fort probable que tout le code qui le précède ne soit pas nécessaire. En supposant cela, sautons tout jusqu'à ce que nous arrivions au strlen que je vous ai écrit auparavant et en particulier, lorsque nous sommes sur cet appel,
00413532 |. PUSH 0 00413534 |. PUSH EAX 00413535 |. CALL WINZIP32.004E68F0; cet appel -------------------- ~ snip ~ -------------------------- 00413554 |> MOV ESI, 12C 00413559 |. PUSH ESI 0041355A |. LEA EAX, DWORD PTR SS: [EBP-144] 00413560 |. PUSH EAX 00413561 |. PUSH EDI 00413562 |. CALL WINZIP32.00411EA8; alors analysons ça |
EAX est «bcom», une chaîne que l'on retrouve relativement souvent dans ces instructions: ne soyons pas dupes, c'est inutile est string => même l'appel est inutile. Si vous voulez bien l'analyser, mais sachez que c'est une des façons dont le programme nous fait perdre un temps précieux: il faut être rapide pour trouver l'algo, sinon on s'ennuiera et on le lâchera. Et ce n'est pas bon =). Cependant, le deuxième appel contient déjà quelque chose qui peut attirer l'attention: en prenant un aperçu, on remarque également une constante, 0x1021. Si l'un d'entre vous avait déjà inversé winzip 8 ou 9, vous aurez sûrement remarqué qu'il s'agissait du masque utilisé par le programme pour ensuite générer la série. Une ampoule devrait déjà s’allumer ici. mais si on ne sait rien de la technique de protection des anciennes versions comment comprenez-vous? Pas de problème, passons à l'appel et voyons quel numéro de série est généré:
---------- ~ snip des boucles précédentes, nous nous intéressons uniquement à la série déjà générée et terminée ------------------- 00411EF9 |. PUSH ECX; / Arg5 00411EFA |. MOVZX EAX, AX; | 00411EFD |. PUSH EAX; | Arg4 00411EFE |. PUSH WINZIP32.0051D9E8; | Arg3 = 0051D9E8 ASCII "% 04X% 04X" 00411F03 |. PUSH DWORD PTR SS: [EBP + 10]; | Arg2 00411F06 |. MOV BYTE PTR DS: [54F4C8], 1; | 00411F0D |. PUSH DWORD PTR SS: [EBP + C]; | Arg1 00411F10 |. CALL WINZIP32.0050755E; \ WINZIP32.0050755E ---------- ~ snip ~ ------------------------ |
Et ici dans la pile, immédiatement après l'appel dont (note pour le plus agrammatique: ce n'est pas une erreur, "cui" pas "ici") ci-dessus - qui est en fait un sprintf très simple - on retrouve notre belle série:
" 56AE08A3 "
Ce qui est en fait ma série pour les versions précédentes de winzip .. si vous ne le saviez pas, vous pouvez toujours essayer de la saisir en tant que série pour obtenir un gentil mendiant, ce qui vous dit peut-être même que votre série est ancienne et que vous devriez mettez-le à jour pour la version 10 du programme. Libéré de l'appel, on voit que quelques petites choses sont faites au serial généré il y a peu de temps, et d'autres appels se succèdent, inutiles, les uns après les autres, jusqu'à ce que nous arrivions enfin à notre série:
004135CB |. MOV EBX, WINZIP32.0054F454; ASCII "1234567890" 004135D0 |. PUSH EBX 004135D1 |. PUSH EDI 004135D2 |. CALL WINZIP32.004120FB; nous devrions jeter un oeil ici =) |
Maintenant, exactement comme nous l'avons fait auparavant avec notre nom d'utilisateur, maintenant nous commençons à voir si les appels immédiatement en dessous de notre série sont intéressants ou non. Commençons par le premier:
00412118 |. MOV DWORD PTR SS: [EBP-150], EAX 0041211E |. MOV DWORD PTR SS: [EBP-14C], EAX 00412124 |. MOV DWORD PTR SS: [EBP-154], EAX 0041212A |. MOV DWORD PTR SS: [EBP-158], 0FFFF 00412134 |. CALL WINZIP32.004E8F04; oublions cet appel, trop compliqué =) ------------- ~ snip ~ --------------------- 0041214B |> / MOVZX EAX, BYTE PTR DS: [EDI]; prend le premier caractère du nom d'utilisateur 0041214E |. | PUSH EAX 00412154 |. | TEST EAX, EAX; si eax == 1 00412156 |. | POP ECX 00412157 |. | JE SHORT WINZIP32.0041215E 00412159 |. | MOV AL, BYTE PTR DS: [EDI]; le char va bien et .. 0041215B |. | MOV BYTE PTR DS: [ESI], AL; ..est stocké dans une autre variable 0041215D |. | INC ESI 0041215E |> | INC EDI 00412162 |. ^ \ JNZ SHORT WINZIP32.0041214B; refaire jusqu'à la fin du nom d'utilisateur 00412164 |> LEA EAX, DWORD PTR SS: [EBP-148]; nom d'utilisateur dans eax 0041216A |. PUSH EAX 0041216B |. MOV BYTE PTR DS: [ESI], 0 0041216E |. CALL WINZIP32.004E6950; renvoie strlen (nom d'utilisateur) 00412173 |. CMP EAX, 12C; si inférieur à 0x12C .. 00412178 |. POP ECX 00412179 |. JB SHORT WINZIP32.0041218C; ..alors ok 00412192 |. PUSH EAX; eax = nom d'utilisateur 00412193 |. CALL WINZIP32.00507CB3; CharLower (nom d'utilisateur) --------------------- ~ blabla ~ ------------------ |
Voici donc un code commenté: je n'y mets que le début de l'appel car il est très long. Si vous avez avancé même un peu superficiellement dans le reste du code, vous remarquerez que d'autres instructions très similaires ou identiques à celles que nous avons analysées auparavant sont répétées, tandis que d'autres totalement nouvelles et que, à première vue, peuvent sembler importantes. Mais comment comprendre que l'appel est presque inutile? Disons que tout d'abord soit on passe un peu le cul, soit on voit que beaucoup, beaucoup de calculs sont effectués, mais au final ni notre série ni notre nom d'utilisateur, ni les longueurs qui peuvent y correspondre ni rien du tout n'apparaissent plus. C'est à ce stade que nous devons analyser un autre appel pour voir si au moins quelque chose s'y passe: sinon, l'appel manqué était probablement important =). Voici le prochain appel:
004135D7 |. LEA EAX, DWORD PTR SS: [EBP-438] 004135DD |. PUSH EBX 004135DE |. PUSH EAX 004135DF |. CALL WINZIP32.00411E19 |
Entrons à l'intérieur:
00411E19 / $ PUSH EBP 00411E1A |. MOV EBP, ESP 00411E1C |. PUSH DWORD PTR SS: [EBP + 8] 00411E1F |. CALL WINZIP32.00507CB3; CharLower (nom d'utilisateur) est important! 00411E24 |. POP ECX 00411E25 |. PUSH DWORD PTR SS: [EBP + C] 00411E28 |. MOV ECX, DWORD PTR DS: [55CE00] 00411E2E |. PUSH DWORD PTR SS: [EBP + 8] 00411E31 |. PUSH WINZIP32.0051D9E0; ASCII "SU_STD" 00411E36 |. CALL WINZIP32.004975F8; nous devons absolument intervenir ici 00411E3B |. TEST AL, AL 00411E3D |. JE SHORT WINZIP32.00411E48 00411E3F |. MOV BYTE PTR DS: [54E5FB], 1 00411E46 |. JMP SHORT WINZIP32.00411E79 00411E48 |> PUSH DWORD PTR SS: [EBP + C] 00411E4B |. MOV ECX, DWORD PTR DS: [55CE00] 00411E51 |. PUSH DWORD PTR SS: [EBP + 8] 00411E54 |. PUSH WINZIP32.0051D9D8; ASCII "SU_PRO" 00411E59 |. CALL WINZIP32.004975F8; et puis ici 00411E5E |. TEST AL, AL 00411E60 |. JE SHORT WINZIP32.00411E72 00411E62 |. MOV BYTE PTR DS: [54E5FB], 1 00411E69 |. MOV BYTE PTR DS: [54E5FD], 1 00411E70 |. POP EBP 00411E71 |. RETN 00411E72 |> MOV BYTE PTR DS: [54E5FB], 0 00411E79 |> MOV BYTE PTR DS: [54E5FD], 0 00411E80 |. POP EBP 00411E81 \. RETN |
Eh bien bien bien .. SU_STD = STANDARD et SU_PRO = PROFESSIONNEL .. au moins théoriquement =). Dans un moment, nous découvrirons si c'est vraiment le cas ou non.
Alors, commençons par entrer dans le premier appel signalé, celui immédiatement en dessous de SU_STD:
-------------------- ~ snip ~ ------------------------- 00497641 |. FF70 08 PUSH DWORD PTR DS: [EAX + 8] 00497644 |. FF70 04 PUSH DWORD PTR DS: [EAX + 4]; "----- BEGIN RSA PUBLIC KEY ----- MBcCEADjFVf2ZJjxOFl + bqOr3O8CAwEAAQ == ----- END RSA PUBLIC KEY -----" 00497647 |. FF37 PUSH DWORD PTR DS: [EDI] 00497649 |. E8 A9C80600 APPEL WINZIP32.00503EF7 |
Hé hé, qu'est-ce que mes yeux voient! RSA? Si belle! =)
Ce qui est poussé est le contenu d'une clé PEM. Pour certains, une question se posera: qu'est-ce qu'une clé PEM? Disons que c'est un fichier .pem qui est utilisé pour stocker les clés rsa nécessaires pour crypter et décrypter les données. Dans ce cas, nous avons une jolie clé publique (pour des informations détaillées sur le rsa, je vous renvoie au tutoriel evilcry sur le sujet, vraiment sympa). Gardons donc les yeux ouverts pour toutes les fonctions inhérentes à RSA.
Si nous jetons un œil à l'intérieur de l'appel, nous verrons que la bibliothèque winzip appelée wzeay32 contient les fonctions concernant le rsa, et, juste pour donner un exemple, il y a deux appels aux fonctions appelées d2i_RSAPublicKey et PEM_ASN1_read_bio. En effectuant une toute petite recherche sur Internet, puisque j'avais remarqué la nette similitude entre des bibliothèques telles que libeay32 et wzeay32, vous pouvez facilement découvrir que wzeay32 est utilisé pour gérer les fonctions OpenSSL dans winzip =). Gardons à l'esprit que nous en aurons besoin plus tard pour créer le keygen (bien que ce ne soit pas essentiel).
Maintenant, commençons à avancer, en gardant à l'esprit que lorsque nous rencontrons du code dans lequel une chaîne est poussée qui a comme contenu ".. \ .. \ common \ WzReg.c" ou quelque chose de similaire, l'appel immédiatement précédent est inutile et donc ne le faisons pas perdre du temps à l'analyser. Cela dit, continuons à avancer, jetons un coup d'œil en appuyant sur Entrée lorsque nous sommes en ligne pour voir si cela fait du bien, et sinon, continuez jusqu'à ce que nous arrivions ici:
00497760 |. PUSH EAX 00497761 |. PUSH DWORD PTR SS: [EBP-44] 00497764 |. PUSH DWORD PTR DS: [EDI] 00497766 |. CALL WINZIP32.00503FC6; et voici la partie importante! |
Voyons ce qui nous attend:
00503FEC |. CMP DWORD PTR SS: [EBP + C], 0; nom d'utilisateur = 0? 00503FF0 |. JE WINZIP32.005041EB; si oui, mendiant 00503FF6 |. TEST BYTE PTR DS: [ESI + 8], 2 00503FFA |. JE WINZIP32.00504197 00504000 |. MOV EDI, DWORD PTR SS: [EBP + 14]; serial = 0? 00504003 |. TEST EDI, EDI 00504005 |. JE WINZIP32.005041BB; si oui, mendiant 0050400B |. CMP DWORD PTR SS: [EBP + 18], 1E; longueur de série = 1E? |
Voici une chose fondamentale: notre série a une longueur de 0x0B (car le zéro final est également compté) alors qu'elle devrait contenir des caractères 0x1E. Faisons F9 et insérons cette série: 12345678901234567890123456789. Ok, revenons à l'endroit où nous étions auparavant, prenons quelques autres vérifications qui ont réussi, jusqu'à ce que nous atteignions cette boucle:
00504039 |> / CMP EDX, 100 00504041 |. | MOV AL, BYTE PTR DS: [ECX + EDI] 00504044 |. | CMP AL, 2D 00504046 |. | JE SHORT WINZIP32.00504050 00504048 |. | MOV BYTE PTR SS: [EBP + EDX-138], AL 00504050 |> | INC ECX 00504051 |. | CMP ECX, 1D 00504054 |. ^ \ JL SHORT WINZIP32.00504039 |
Je ne l'ai pas commenté car il y a peu à comprendre: il supprime simplement de la série tous les tirets qui devraient être là, mais que nous n'avons pas encore mis, et repositionne les caractères dans la chaîne pour qu'il n'y ait pas de "trous" (nous venons de supprimer les tirets, alors ..). Gardez toujours à l'esprit que 0x2D = '-'. Immédiatement après cette boucle, il y a un bel appel que nous allons analyser en profondeur car il est fondamental. Voyons comment ça commence:
00504FDD |> / MOV EAX, EDI; longueur en série sans tirets dans eax 00504FDF |. | SHL EAX, 3; eax * 8 00504FE2 |. | PUSH 5 00504FE4 |. | CDQ 00504FE5 |. | POP ECX 00504FE6 |. | IDIV ECX; eax / 5 avec reste dans edx 00504FE8 |. | EDX, TEST EDX; reste = 0? (donc eax multiple de 5?) 00504FEA |. | JNZ WINZIP32.005050F5; sinon, saute et mendiant |
Eh bien, nous devons donc mettre beaucoup de tirets jusqu'à ce que notre 29 devienne un multiple de 5. Alors mettons 4 jolis tirets et faisons notre série comme ceci: 12345-78901-34567-90123-56789
Attention à REMPLACER les caractères qui étaient là avant avec les tirets, et de ne pas les ajouter, sinon le chèque que nous avons analysé avant donne mendiant off =). Le code que je vous montre maintenant est très long, mais n'ayez pas peur, j'explique les choses vraiment importantes là-dedans et immédiatement après je vais tout clarifier en vous l'expliquant sans les commentaires, car ce serait un gâchis monstrueux à faire les deux choses ensemble:
00505011 |> / MOV EAX, DWORD PTR SS: [EBP + 8] 00505014 |. | MOVZX EAX, BYTE PTR DS: [EAX] 00505017 |. | PUSH EAX 00505018 |. | CALL WINZIP32.004E9127; oublie ça, inutile 0050501D |. | CMP AL, 41; al = caractère de la série 00505020 |. | JNZ SHORT WINZIP32.00505026; .. il le travaille pour lui donner de la valeur. 00505022 |. | XOR CL, CL; ..pour que cela se forme toujours. 00505024 |. | JMP SHORT WINZIP32.00505062; ..à partir de seulement 5 bits significatifs. 00505026 |> | CMP AL, 42 00505028 |. | JBE SHORT WINZIP32.00505032 0050502A |. | CMP AL, 49 ans 0050502E |. | SUB AL, 42 00505030 |. | JMP SHORT WINZIP32.00505060 00505032 |> | CMP AL, 49 00505034 |> | JBE SHORT WINZIP32.0050503E 00505036 |. | CMP AL, 4F 00505038 |. | JNB SHORT WINZIP32.00505040 0050503A |. | SUB AL, 43 0050503E |> | CMP AL, 4F 00505040 |> | JBE SHORT WINZIP32.0050504A 00505042 |. | CMP AL, 53 ans 00505044 |. | JNB SHORT WINZIP32.0050504C 00505046 |. | SUB AL, 44 00505048 |. | JMP SHORT WINZIP32.00505060 0050504A |> | CMP AL, 53 0050504E |. | CMP AL, 5A 00505050 |. | JA SHORT WINZIP32.00505056 00505052 |. | SUB AL, 45 00505054 |. | JMP SHORT WINZIP32.00505060 00505056 |> | CMP AL, 30; Commutateur (cas 30..39) 00505058 |. | JB SHORT WINZIP32.005050D3 0050505A |. | CMP AL, 39 ans 0050505E |. | SUB AL, 1A; Cas 30 («0»), 31 («1»), 32 («2»). 00505060 |> | MOV CL, AL 00505062 |> | MOV EAX, DWORD PTR SS: [EBP + C]; après quoi toutes les valeurs obtenues .. 00505065 |. | PUSH 8; ils sont "mixtes" comme expliqué plus loin 00505067 |. | CDQ 00505068 |. | POP EBX 00505069 |. | IDIV EBX 0050506B |. | CMP EDX, 7; Commutateur (cas 0..7) 0050506E |. | JA SHORT WINZIP32.005050BF 00505070 |. | JMP DWORD PTR DS: [EDX * 4 + 505104] 00505077 |> | SHL CL, 3; Cas 0 de l'interrupteur 0050506B 0050507A |. | JMP SHORT WINZIP32.005050B8 0050507E |. | SHR AL, 2 00505081 |. | OU BYTE PTR DS: [ESI], AL 00505083 |. | INC ESI 00505084 |. | SHL CL, 6 00505087 |. | JMP SHORT WINZIP32.005050B8 00505089 |> | SHL CL, 1; Cas 2 de l'interrupteur 0050506B 0050508B |. | JMP SHORT WINZIP32.005050A9 0050508D |> | MOV AL, CL; Cas 3 de l'interrupteur 0050506B 00505092 |. | OU BYTE PTR DS: [ESI], AL 00505094 |. | INC ESI 00505095 |. | SHL CL, 4 00505098 |. | JMP SHORT WINZIP32.005050B8 0050509A |> | MOV AL, CL; Cas 4 de l'interrupteur 0050506B 0050509E |. | OU BYTE PTR DS: [ESI], AL 005050A0 |. | INC ESI 005050A1 |. | SHL CL, 7 005050A4 |. | JMP SHORT WINZIP32.005050B8 005050A6 |> | SHL CL, 2; Cas 5 de l'interrupteur 0050506B 005050A9 |> | OU BYTE PTR DS: [ESI], CL 005050AB |. | JMP SHORT WINZIP32.005050BF 005050AD |> | MOV AL, CL; Cas 6 de l'interrupteur 0050506B 005050AF |. | SHR AL, 3 005050B2 |. | OU BYTE PTR DS: [ESI], AL 005050B4 |. | INC ESI 005050B5 |. | SHL CL, 5 005050B8 |> | MOV BYTE PTR DS: [ESI], CL 005050BA |. | JMP SHORT WINZIP32.005050BF 005050BC |> | OU BYTE PTR DS: [ESI], CL; Cas 7 de l'interrupteur 0050506B 005050BE |. | INC ESI 005050BF |> | MOV EBX, DWORD PTR SS: [EBP-4]; Cas par défaut du commutateur 0050506B 005050C2 |. | DEC EDI 005050C3 |. | INC DWORD PTR SS: [EBP + 8] 005050C6 |. | INC DWORD PTR SS: [EBP + C] 005050C9 |> | TEST EDI, EDI 005050CB |. ^ \ JG WINZIP32.00505011 |
Ok, essayons d'expliquer organiquement: que se passe-t-il au début de la boucle? En pratique, il prend les caractères du nom d'utilisateur et soustrait une valeur par défaut du code ascii en fonction de la valeur de caractère extraite de la série. Par exemple, si le caractère est un nombre (et est donc compris entre 0x30 et 0x39), il soustrait 0x1A et ainsi de suite. Cela sert à avoir une valeur où les bits significatifs sont au maximum 5. Après cela, dans la deuxième partie de la boucle si vous remarquez qu'il y a un commutateur avec 8 maisons .. 8 comme les bits qui composent un octet non? =) Bref, voici pratiquement ce que ça fait: imaginez courir
Nous avons ces 3 octets
10011011 11110101 10111100
À travers la première partie de la boucle, nous les transformons en:
00011011 00010101 00011100
À travers la deuxième partie de la boucle, nous supprimons les 3 bits initiaux et transformons tout en ceci:
11011 10101 11100
Alors maintenant, le premier octet, juste pour donner un exemple, sera:
11011101
Et ainsi de suite pour tous les autres octets. Comme vous pouvez le voir, c'est très différent de ce que nous avions auparavant (100011011). Mais les caractères sont des multiples de 5 (contrairement à mon exemple) et donc au final en supprimant 3 bits de chaque octet, il est possible de former un nombre "droit" d'octets (il ne reste plus de bits qui n'atteignent pas l'octave., comme dans mon exemple). D'accord, j'espère que c'est clair, car c'était une partie importante =) (sinon, relisez le tout ou envoyez-moi un e-mail).
Nous arrivons ici à une autre partie importante: la génération d'un octet particulier. Jusqu'à présent, nous avons vu que le programme prend notre série, le modifie de manière appropriée pour obtenir une chaîne de 120 bits (15 caractères ASCII). Cette chaîne, notre "série numérique", a un octet final qui doit être très particulier. Le programme, dans le morceau de code ci-dessous, se donne pour tâche de vérifier sa validité effective; voyons selon quels paramètres:
005040A5 |> / MOV EAX, DWORD PTR DS: [EDI]; voyons ce que vaut edi sous peu .. 005040A7 |. | TEST EAX, EAX 005040A9 |. | MOV BL, 1; méfiez-vous de ce mov, il est à mettre dans le keygen 005040AB |. | JLE SHORT WINZIP32.005040E3 005040AD |. | MOV DWORD PTR SS: [EBP-148], EAX 005040B3 |> | / MOV EAX, DWORD PTR SS: [EBP-140] 005040B9 |. || PUSH 8 005040BB |. || CDQ 005040BC |. || POP ECX 005040BD |. || IDIV ECX 005040BF |. || MOV ESI, EDX 005040C1 |. || MOV EDX, 80; 0x80 = 005040C6 |. || MOV ECX, ESI 005040C8 |. || SAR EDX, CL; shr edx, cl 005040CA |. || TEST BYTE PTR SS: [EBP + EAX-14], DL; ebp + eax-14 = notre série numérique 005040CE |. || JE SHORT WINZIP32.005040D2; sauter si le bit = 0 005040D0 |. || INC BL 005040D2 |> || INC DWORD PTR SS: [EBP-140] 005040D8 |. || DEC DWORD PTR SS: [EBP-148] 005040DE |. ^ | \ JNZ SHORT WINZIP32.005040B3 005040E0 |. | MOV ESI, DWORD PTR SS: [EBP + 8] 005040E3 |> | MOVZX EAX, BL; le nombre de bits dans eax 005040E6 |. | CDQ 005040E7 |. | PUSH 2 005040E9 |. | POP ECX 005040EA |. | IDIV ECX; divisez-le par 2 005040EC |. | MOV ECX, DWORD PTR SS: [EBP-144]; nombre qui est 7 et qui est décrémenté à chaque boucle 005040F2 |. | MOV AL, BYTE PTR SS: [EBP-5]; dernier octet de la série numérique dans al 005040F5 |. | SHL DL, CL 005040F7 |. | XOR AL, DL 005040F9 |. | TEST DL, AL; (dernier octet) et (valeur de dl) 005040FB |. | JNZ WINZIP32.0050418B; si ça saute, mendiant 00504101 |. | AJOUTER EDI, 4; passer au dword suivant 00504104 |. | DEC DWORD PTR SS: [EBP-144]; diminuer le nombre dont je parlais plus tôt 0050410A |. | CMP EDI, WINZIP32.0054B734; ASCII "WZRFWZUD" 00504110 |. ^ \ JL SHORT WINZIP32.005040A5; lorsque le nombre est inférieur à 3, la boucle s'arrête |
Donc, comme vous pouvez le voir, il s'agit d'une double boucle. Je vais vous expliquer maintenant comment cela fonctionne. Fondamentalement, il prend le nombre 0x80 (qui est un nombre qui est transformé en binaire vaut 1000000) et fait un et ('test') vérifie si le premier bit du premier octet de notre "série numérique" est égal à 1. Si c'est, je ne saute pas et bl, qui prend en compte tous les bits qui sont 1 dans les 25 premiers bits de la série numérique, est incrémenté. Pourquoi ai-je mentionné les 25 premiers bits? Voyons ce que contient edi: les 4 premiers dwords sont égaux à 0x19 et le dernier à 0x14. Si vous ajoutez tout cela, vous voyez que c'est 0x78, qui est 120. Comme nos bits =). De cela, nous comprenons que winzip compte combien de bits = 1 il y a dans les 25 premiers bits de la série numérique, puis dans les 25 suivants et ainsi de suite jusqu'à ce qu'il n'en reste plus que 20.
Ok, nous avons presque terminé, attendez =). Si nous regardons le reste de l'appel dans lequel nous sommes, nous voyons deux appels intéressants: RSA_public_decrypt et SHA1.
RSA_private_encrypt, RSA_public_decrypt - opérations de signature de bas niveau
#include < openssl / rsa.h >
int RSA_private_encrypt (int flen, unsigned char * from,
unsigned char * to, RSA * rsa, int padding);
int RSA_public_decrypt (int flen, unsigned char * from,
unsigned char * to, RSA * rsa, int padding);
Ces fonctions gèrent les signatures RSA à un niveau bas.
RSA_private_encrypt () signe les Flen octets à partir (généralement un condensé de message avec un algorithme d' identification) à l' aide de la clé privée rsa et stocke la signature à . to doit pointer vers RSA_size (rsa) octets de mémoire.
padding désigne l'un des modes suivants:
RSA_PKCS1_PADDING
Rembourrage PKCS # 1 v1.5. Cette fonction ne gère pas l' algorithmeIdentifier spécifié dans PKCS # 1. Lors de la génération ou de la vérification des signatures PKCS # 1, rsa_sign (3) et rsa_verify (3) doivent être utilisés.
RSA_NO_PADDING
Signature RSA brute. Ce mode doit uniquement être utilisé pour mettre en œuvre des modes de remplissage du son cryptographiquement code d'application. La signature directe des données utilisateur avec RSA n'est pas sécurisée.
RSA_public_decrypt () récupère le condensé de la Flen octets de long signature à partir en utilisant la clé publique du signataire rsa . to doit pointer vers une section mémoire suffisamment grande pour contenir le résumé du message (qui est plus petit que RSA_size (rsa) - 11 ). padding est le mode de remplissage utilisé pour signer les données.
RSA_private_encrypt () renvoie la taille de la signature (c'est-à-dire RSA_size (rsa)). RSA_public_decrypt () renvoie la taille du résumé de message récupéré.
En cas d'erreur, -1 est renvoyé; les codes d'erreur peuvent être obtenus par err_get_error (3).
C'est la description des fonctions RSA_public_decrypt et des RSA_private_encrypt complémentaires (ce dernier que nous utiliserons dans le keygen). Donc notre fameux "série numérique" doit être une valeur retournée par RSA_private_encrypt, après quoi le programme va le déchiffrer et obtenir un nombre. Voyons tout cela, et voyons aussi bientôt ce qui est fait avec ce numéro:
00504112 |. | PUSH 3 00504114 |. | PUSH DWORD PTR DS: [ESI + 4] 00504117 |. | LEA EAX, DWORD PTR SS: [EBP-24] 0050411A |. | PUSH EAX; valeur de retour, nous faisons un Follow in dump sur EAX 0050411B |. | LEA EAX, DWORD PTR SS: [EBP-14] 0050411E |. | PUSH EAX 00504121 |. | POP ESI 00504122 |. | PUSH ESI 00504123 |. | APPEL DWORD PTR DS: [55EF28]; wzeay32.RSA_public_decrypt |
Si vous avez fait le suivi d'un vidage, gardez à l'esprit la valeur du premier dword, car nous le retrouverons bientôt.
En allant un peu plus loin dans la liste, il y a la fonction de hachage SHA1. Si j'étais vous, j'aurais déjà deviné ce qui va se passer, mais je ne gâcherai pas encore la surprise: P.
SYNOPSIS
#include <openssl / sha.h>
unsigned char * SHA1 (const unsigned char * d, unsigned long n,
unsigned char * md);
LA DESCRIPTION
SHA1()
calcule le résumé de message SHA-1 des n octets en
d et le place dans md (qui doit avoir de l'espace pour SHA_DIGEST_LENGTH == 20 octets de sortie). Si md est NULL, le condensé est placé dans un tableau statique.
Maintenant que nous savons quels paramètres prend la fonction de hachage, voyons le désasme:
00504140 |. | PUSH EAX; variable où contenir le hachage 00504141 |. | PUSH DWORD PTR SS: [EBP + 10]; longueur du hachage (7 dans le cas de Graftal) 00504144 |. | PUSH DWORD PTR SS: [EBP + C]; string to hashare (greffe en LOWERCASE je recommande) 00504147 |. | APPEL DWORD PTR DS: [55EF20]; CALL un hachage SHA1 0050414D |. | PUSH ESI 0050414E |. | LEA EAX, DWORD PTR SS: [EBP-14] 00504151 |. | PUSH 0 00504153 |. | PUSH EAX 00504154 |. | CALL WINZIP32.004E68F0 00504159 |. | PUSH 0E 0050415B |. | LEA EAX, DWORD PTR SS: [EBP-38] 0050415E |. | PUSH EAX 00504162 |. | PUSH EAX 00504163 |. | CALL WINZIP32.004E7E20 00504168 |. | PUSH ESI 00504169 |. | LEA EAX, DWORD PTR SS: [EBP-24] 0050416D |. | LEA EAX, DWORD PTR SS: [EBP-14] 00504170 |. | PUSH EAX 00504171 |. | CALL WINZIP32.004E95C0; voici le vrai chèque |
Tout cela n'était pas vraiment fondamental, nous trouvons la partie importante dans l'appel que j'ai marqué pour vous et que nous allons maintenant analyser, même si ce n'est pas très minutieusement, car cela ne sert à rien:
----- ~ snip ~ ----- 004E961D |. REPE CMPS DWORD PTR ES: [EDI], DWORD PTR DS: [ESI]; esi = hash de notre nom d'utilisateur, edi = hash extrait de la série 004E961F |. JE SHORT WINZIP32.004E9648; s'ils sont les mêmes, sautez et le numéro de série est correct, ole! ----- ~ snip ~ ----- |
Hé hé hé! Qu'est ce que je vois! =)
Dans edi se trouve la valeur trouvée par la fonction RSA_public_decrypt! Mais c'est gentil hein? Voici le "secret": si la fonction RSA renvoie le hash de notre nom d'utilisateur, nous sommes enregistrés =).
Ok, on a beaucoup parlé du RSA mais .. tu ne penses pas qu'il manque encore quelque chose pour compléter le tableau? Ouais, qu'en est-il des clés? Où les mettons-nous? Lesquels sont-ils? =). Toutes les questions auxquelles je vais répondre sous peu. Tout d'abord, rappelez-vous que le programme avait deux modes d'enregistrement,
----- ~ snip ~ ----- 00504112 |. 6A 03 | PUSH 3 00504114 |. FF76 04 | PUSH DWORD PTR DS: [ESI + 4]; Follow in dump sur cette variable 00504117 |. 8D45 DC | LEA EAX, DWORD PTR SS: [EBP-24] 0050411A |. 50 | PUSH EAX 0050411B |. 8D45 EC | LEA EAX, DWORD PTR SS: [EBP-14] 0050411E |. 50 | PUSH EAX 00504121 |. 5E | POP ESI 00504122 |. 56 | PUSH ESI 00504123 |. FF15 28EF5500 | APPEL DWORD PTR DS: [55EF28]; RSA_public_decrypt ----- ~ snip ~ ----- |
Comme il s'agit d'une structure bignum, cela peut être un peu déroutant au début, mais en regardant un peu plus bas notre joli Follow in dump, nous pouvons voir une belle rangée de nombres longs 0x0F qui sont également pointés un peu plus haut dans la structure. En l'occurrence, la fonction prend 0x0F octets. Sera-ce une coïncidence? Naa: P. Evidemment, marquez ces octets au contraire et voici la première clé, notre N pour le SU_STD: E31557F66498F138597E6EA3ABDCEF.
Répétant la même chose pour le SU_PRO: 9D845872E117501775DD51D9044945.
Maintenant, utilisez RSA Tool v2 (si vous ne l'avez pas, recherchez-le sur Google et trouvez-le, il est relativement bien connu) et dites-lui de trouver p et q, sans vous soucier de l'exposant qui est l'habituel 0x10001. J'écris toutes les données ici:
SU_STD:
n = E31557F66498F138597E6EA3ABDCEF
p = EC5A57BA9E77CFB
q = F5F5BE226C8F59D
e = 0x10001
d = 16B67CCCD53502F0870D775052FB49
SU_PRO:
n = 9D845872E117501775DD51D9044945
p = C2C17D26DBC6E83
q = CF0D008F6995E97
e = 0x10001
d = 1E84F8063A2AA42B29DD5578421E79
Ils sont tous dérivés de l'outil RSA. Avec ces données, nous pouvons enfin coder le keygen =).
~ KeyGen ~
Concernant le keygen, il n'y a pas grand chose à dire: je résume ici les étapes du keygen et j'attache le src au keygen que j'ai créé. Je ne veux pas, j'avais commencé à écrire le keygen en asm, mais quand j'ai décidé d'implémenter OpenSSL j'ai préféré l'écrire en C: par conséquent, certaines pièces sont faites en asm dans le source, mais elles se comprennent assez bien; disons simplement que c'est anti-esthétique. Et puis si vous ne les comprenez pas, qu'allez-vous inverser? : P
Ok alors, notre keygen doit faire exactement ceci:
1) Prenez le nom d'utilisateur et mettez-le en minuscules.
2) Hash la chaîne obtenue.
3) Initialisez les clés RSA.
4) Faites un RSA_private_encrypt sur notre hachage.
5) Créez le dernier octet en comptant les bits = 1 présents dans les autres.
6) Créez la série en caractères en effectuant le processus inverse effectué par le programme et en mettant les tirets.
7) Tout cela pour les deux types de séries bien sûr, standard et professionnel.
Ici, comme devoirs, codez un joli keygen propre et envoyez-moi le src comme d'habitude au mail latfarg (at) gmail (dot) com =).
Greffe
Notes finales |
C'était le tutoriel, j'espère que vous l'avez apprécié : P. Il m'a fallu beaucoup de temps pour l'écrire en raison du manque de temps ( : P), mais à la fin je l'ai fait. Je voudrais remercier tous ceux que je connais, en particulier Eimiar, le mythique cri diabolique ; ), le clan UT2004 auquel j'appartiens et comme toujours le Que qui publie très gentiment mon matériel: P.
Si vous avez des commentaires, des conseils, des doutes, envoyez tout à latfarg (at) gmail (dot) com = )
À la prochaine.
Avertissement |
Je vous rappelle que le logiciel doit être acheté et non volé, vous devez enregistrer votre produit après la période d'évaluation. Je ne suis pas responsable des dommages causés à votre ordinateur par une mauvaise utilisation de ce didacticiel. Ce document a été écrit pour inciter le consommateur à enregistrer légalement ses programmes, et non pour lui faire utiliser les nombreux fichiers de crack sur le net, en fait ce document aide à comprendre l'effort que chaque développeur a dû faire pour fournir son consommateurs respectifs, les meilleurs produits possibles.
Nous l'inversons à titre informatif uniquement et pour améliorer nos connaissances du langage d'assemblage.